package com.icare.common.service.autoconfig;

import com.icare.common.service.properties.RedisProperties;
import com.icare.common.service.utils.RedisCacheUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.*;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

import java.util.Arrays;

/**
 * 描述:Redis自动装配
 * 创建者:田海波 于 2018年05月31日 10:48.
 */
@Configuration
@ConditionalOnBean(RedisProperties.class)
public class RedisAutoConfig {

    private static Log log = LogFactory.getLog(RedisAutoConfig.class);

    @Autowired
    private RedisProperties redisProperties;

    @Bean
    public StringRedisTemplate initRedisTemplate(){
        dealProperties();
        JedisConnectionFactory jedisConnectionFactory = createJedisConnectionFactory();
        StringRedisTemplate  redisTemplate = new StringRedisTemplate();
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        redisTemplate.setDefaultSerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        RedisCacheUtil.setRedisTemplate(redisTemplate);
        log.info("Initializing StringRedisTemplate success!");
        return redisTemplate;
    }

    /**
     * 根据配置不同，创建和设置Jedis连接工厂
     * @return
     */
    private JedisConnectionFactory createJedisConnectionFactory(){
        JedisConnectionFactory jedisConnectionFactory = null;
        JedisPoolConfig jedisPoolConfig = initJedisPoolConfig();
        String[] nodes = redisProperties.getNodes().replaceAll(" ", "").split(",");
        if(nodes.length == 1){
            JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder().usePooling().poolConfig(jedisPoolConfig).build();
            jedisConnectionFactory = new JedisConnectionFactory(createStandaloneConfig(nodes),jedisClientConfiguration);
            log.info("redis mode -> Standalone");
        }else if(nodes.length > 1){
            if("Cluster".equalsIgnoreCase(redisProperties.getClusterOrSentinel())){
                jedisConnectionFactory = new JedisConnectionFactory(createClusterConfig(nodes),jedisPoolConfig);
                log.info("redis mode -> Cluster");
            }else if("Sentinel".equalsIgnoreCase(redisProperties.getClusterOrSentinel())){
                jedisConnectionFactory = new JedisConnectionFactory(createSentinelConfig(nodes,jedisPoolConfig),jedisPoolConfig);
                log.info("redis mode -> Sentinel");
            }else{
                throw new RuntimeException("redis config [clusterOrSentinel] can has 'Cluster' or 'Sentinel'.");
            }
        }else{
            throw new RuntimeException("redis config [nodes] at lease has one node like '127.0.0.1:6379'.");
        }
        return jedisConnectionFactory;
    }

    /**
     * 创建Redis集群配置
     * 并且设置最大重定向次数，集群机器之间会根据操作的key的CRC16计算值记性槽点定位，并跳转到相应的机器
     * @param nodes
     * @return
     */
    private RedisClusterConfiguration createClusterConfig(String[] nodes){
        RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(Arrays.asList(nodes));
        redisClusterConfiguration.setMaxRedirects(redisProperties.getMaxRedirects());
        return redisClusterConfiguration;
    }

    /**
     * 创建redis哨兵配置,一主多备
     * @param nodes
     * @return
     */
    private RedisSentinelConfiguration createSentinelConfig(String[] nodes,JedisPoolConfig jedisPoolConfig){
        RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
        for (String s:nodes) {
            String[] split = s.split(":");
            if(split.length != 2)
                continue;
            redisSentinelConfiguration.addSentinel(new RedisNode(split[0],Integer.valueOf(split[1])));
        }
        redisSentinelConfiguration.setMaster(redisProperties.getSentinelMaster());
        redisSentinelConfiguration.setPassword(getRedisPassword());
        redisSentinelConfiguration.setDatabase(redisProperties.getDb());
        return redisSentinelConfiguration;
    }

    /**
     * 创建redis单节点配置
     * @param nodes
     * @return
     */
    private RedisStandaloneConfiguration createStandaloneConfig(String[] nodes){
        String[] node = nodes[0].split(":");
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(node[0],Integer.valueOf(node[1]));
        redisStandaloneConfiguration.setPassword(getRedisPassword());
        redisStandaloneConfiguration.setDatabase(redisProperties.getDb());
        return redisStandaloneConfiguration;
    }

    /**
     * 创建jedis连接池配置，设置池最大限制大小
     * @return
     */
    private JedisPoolConfig initJedisPoolConfig(){
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(redisProperties.getMaxTotal() == null?50:redisProperties.getMaxTotal());
        return config;
    }

    /**
     * 封装redis密码
     * @return
     */
    private RedisPassword getRedisPassword(){
        return RedisPassword.of(redisProperties.getPassword());
    }

    /**
     * 预处理自定义配置
     */
    private void dealProperties() {
        if(redisProperties.getTimeout() == null){
            redisProperties.setTimeout(3000);
        }
        if(redisProperties.getDb() == null){
            redisProperties.setDb(0);
        }
        if(redisProperties.getPassword() != null && redisProperties.getPassword().replaceAll(" ","").equals("")){
            redisProperties.setPassword(null);
        }
    }
}
